#include <iostream>          //用于输入输出操作
#include <fcntl.h>           //用于文件控制选项，如打开文件的权限设置。
#include <linux/videodev2.h> //V4L2 (Video for Linux 2)接口的定义，用于视频设备的操作。
#include <sys/ioctl.h>       // 用于设备控制的ioctl系统调用。
#include <unistd.h>          // 提供对POSIX操作系统API的访问，如文件操作和内存管理。
#include <cstring>           // 提供C风格字符串操作的功能。
#include <sys/mman.h>        // 用于内存映射操作

#define BUFFER_COUNT 4 // 缓冲区数量为4

/*
    Buffer 结构体包含两个成员：
    start：指向缓冲区数据的指针。
    length：缓冲区的长度。
*/
struct Buffer
{
    void *start;
    size_t length;
};

int main()
{
    const char *device = "/dev/video40"; // HDMI输入设备
    int fd = open(device, O_RDWR);       // 以读写方式打开指定设备，并返回文件描述符
    if (fd < 0)
    {
        std::cerr << "无法打开设备" << std::endl;
        return -1;
    }

    struct v4l2_fmtdesc fmt_desc;
    memset(&fmt_desc, 0, sizeof(fmt_desc));
    fmt_desc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    while (ioctl(fd, VIDIOC_ENUM_FMT, &fmt_desc) == 0)
    {
        std::cout << "支持的格式: " << fmt_desc.description << std::endl;
        fmt_desc.index++;
    }

    // 设置视频格式
    struct v4l2_format fmt;                      // 定义一个格式结构体用于设置视频流格式
    memset(&fmt, 0, sizeof(fmt));                // 将fmt结构体清零，以确保初始值为空
    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;      // 设置缓冲区类型为视频捕获
    fmt.fmt.pix.width = 1920;                    // 设置视频宽度为1920像素
    fmt.fmt.pix.height = 1080;                   // 设置视频高度为1080像素
    fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_H264; // 设置像素格式为H.264
    fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;   // 设置字段类型为交错

    if (ioctl(fd, VIDIOC_S_FMT, &fmt) < 0)
    {
        std::cerr << "设置格式失败" << std::endl;
        close(fd);
        return -1;
    }

    // 请求缓冲区
    struct v4l2_requestbuffers req;         // 定义一个请求缓冲区的结构体
    memset(&req, 0, sizeof(req));           // 清零结构体
    req.count = BUFFER_COUNT;               // 设置请求的缓冲区数量为宏定义的值（4）
    req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; // 设置缓冲区类型为视频捕获
    req.memory = V4L2_MEMORY_MMAP;          // 设置缓冲区的内存类型为内存映射（mmap）

    if (ioctl(fd, VIDIOC_REQBUFS, &req) < 0)
    {
        std::cerr << "请求缓冲区失败" << std::endl;
        close(fd);
        return -1;
    }

    // 映射缓冲区
    Buffer buffers[BUFFER_COUNT];
    // 使用循环来请求每个缓冲区
    for (int i = 0; i < BUFFER_COUNT; i++)
    {
        struct v4l2_buffer buf;
        memset(&buf, 0, sizeof(buf));
        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; // 设置缓冲区类型为视频捕获
        buf.memory = V4L2_MEMORY_MMAP;          // 设置缓冲区的内存类型为内存映射（mmap）
        buf.index = i;                          // 当前缓冲区的索引

        if (ioctl(fd, VIDIOC_QUERYBUF, &buf) < 0)
        {
            std::cerr << "查询缓冲区失败" << std::endl;
            close(fd);
            return -1;
        }

        buffers[i].length = buf.length;                                                                  // 保存缓冲区的长度
        buffers[i].start = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset); // 映射到用户空间的起始地址保存到buffers数组
    }

    // 启动流
    enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    if (ioctl(fd, VIDIOC_STREAMON, &type) < 0)
    {
        std::cerr << "启动流失败" << std::endl;
        close(fd);
        return -1;
    }

    // 循环读取数据
    while (true)
    {
        struct v4l2_buffer buf;
        memset(&buf, 0, sizeof(buf)); // 清零buf结构体并设置类型和内存类型
        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        buf.memory = V4L2_MEMORY_MMAP;

        // 等待新帧
        if (ioctl(fd, VIDIOC_DQBUF, &buf) < 0)
        {
            std::cerr << "读取缓冲区失败" << std::endl;
            break;
        }

        // 在此处可以处理解码后的数据
        // buffers[buf.index].start 包含解码后帧数据

        // 重新排队缓冲区
        if (ioctl(fd, VIDIOC_QBUF, &buf) < 0)
        {
            std::cerr << "重新排队缓冲区失败" << std::endl;
            break;
        }
    }

    // 停止流
    ioctl(fd, VIDIOC_STREAMOFF, &type);
    // 解除所有缓冲区的映射
    for (int i = 0; i < BUFFER_COUNT; i++)
    {
        munmap(buffers[i].start, buffers[i].length);
    }
    close(fd);
    return 0;
}
